#ifndef GNSMARTPOINTER_H
#define GNSMARTPOINTER_H
#include "GnMainLibType.h"
#include "GnTypeManip.h"

#include <functional>
#include <stdexcept>

#include <string>
#if !defined(_MSC_VER)
#include <stdint.h>
#endif // #if !defined(_MSC_VER)


#if !defined(_MSC_VER)
#  if defined(__sparc__)
#    include <inttypes.h>
#  else
#    include <stdint.h>
#  endif
#endif // #if !defined(_MSC_VER)



template< class T >
struct DontPropagateConst
{
	typedef T Type;
};

template< class T >
struct PropagateConst
{
	typedef const T Type;
};


template <class T>
class DefaultSPStorage
{
public:
    typedef T* StoredType;    // the type of the pointee_ object
    typedef T* InitPointerType; /// type used to declare OwnershipPolicy type.
    typedef T* PointerType;   // type returned by operator->
    typedef T& ReferenceType; // type returned by operator*

    GNFORCEINLINE DefaultSPStorage() : pointee_(Default())
    {}

    // The storage policy doesn't initialize the stored pointer
    //     which will be initialized by the OwnershipPolicy's Clone fn
   GNFORCEINLINE  DefaultSPStorage(const DefaultSPStorage&) : pointee_(0)
    {}

    template <class U>
   GNFORCEINLINE  DefaultSPStorage(const DefaultSPStorage<U>&) : pointee_(0)
    {}

    DefaultSPStorage(const StoredType& p) : pointee_(p) {}

    PointerType operator->() const { return pointee_; }

    ReferenceType operator*() const { return *pointee_; }

    // Accessors
    template <class F>
    friend typename DefaultSPStorage<F>::PointerType GetImpl(const DefaultSPStorage<F>& sp);

    template <class F>
    friend const typename DefaultSPStorage<F>::StoredType& GetImplRef(
		const DefaultSPStorage<F>& sp);

    template <class F>
    friend typename DefaultSPStorage<F>::StoredType& GetImplRef(DefaultSPStorage<F>& sp);

protected:
	template <class F>
	friend typename DefaultSPStorage<F>::PointerType GetImpl(const DefaultSPStorage<F>* sp);

	template <class F>
	friend const typename DefaultSPStorage<F>::StoredType& GetImplRef(
		const DefaultSPStorage<F>* sp);

	template <class F>
	friend typename DefaultSPStorage<F>::StoredType& GetImplRef(DefaultSPStorage<F>* sp);

protected:
	GNFORCEINLINE void Clone(DefaultSPStorage& rhs)
	{
		pointee_ = rhs.pointee_;
	}
	GNFORCEINLINE void Reset(const StoredType& pointee)
	{
		pointee_ = pointee;
	}

    GNFORCEINLINE  void Destroy()
    {
        GnDelete pointee_;
    }

    GNFORCEINLINE static StoredType Default()
    {
		return 0;
	}

private:
    StoredType pointee_;
};

template <class P>
class RefCounted
{
public:
   GNFORCEINLINE  RefCounted() : pCount_(GnAlloc(uintptr_t, 1))
    {
        GnAssert(pCount_!=0);
        *pCount_ = 1;
    }

    GNFORCEINLINE RefCounted(const RefCounted& rhs) : pCount_(rhs.pCount_)
    {}

    // MWCW lacks template friends, hence the following kludge
    template <typename P1>
    GNFORCEINLINE RefCounted(const RefCounted<P1>& rhs) : pCount_(reinterpret_cast<const RefCounted&>(rhs).pCount_)
    {}

    enum { destructiveCopy = false };

protected:
	inline bool Release(const P&)
	{
		if (pCount_ && !--*pCount_)
		{
			GnFree(pCount_);
			pCount_ = NULL;
			return true;
		}
		return false;
	}

	inline void Clone(RefCounted& rhs)
	{
		pCount_ = rhs.pCount_;
		if(pCount_)
			++*pCount_;
	}
	inline void Reset(const P& val)
	{
		if( val )
		{
			pCount_ = GnAlloc(uintptr_t, 1);
			*pCount_ = 1;
		}
		pCount_ = NULL;
	}

private:
    // Data
    uintptr_t* pCount_;
};



template
<
    typename objectT,
    template <class> class OwnershipPolicy = RefCounted,
    template <class> class StoragePolicy = DefaultSPStorage
 >
 class GnSmartPointer;

template
<
    typename objectT,
    template <class> class OwnershipPolicy = RefCounted,
    template <class> class StoragePolicy = DefaultSPStorage
>
struct GnSmartPointerDef
{
    typedef GnSmartPointer<objectT, OwnershipPolicy, StoragePolicy> type;
};

template
<
    typename objectT,
    template <class> class OwnershipPolicy,
    template <class> class StoragePolicy
>
class GnSmartPointer : public GnMemoryObject, protected StoragePolicy<objectT>,
	protected OwnershipPolicy<typename StoragePolicy<objectT>::InitPointerType>
{
    typedef StoragePolicy<objectT> SP;
    typedef OwnershipPolicy<typename StoragePolicy<objectT>::InitPointerType> OP;

public:
    typedef typename SP::PointerType PointerType;
    typedef typename SP::StoredType StoredType;
    typedef typename SP::ReferenceType ReferenceType;
	typedef typename GnT::Select<OP::destructiveCopy,GnSmartPointer, const GnSmartPointer>::Result
            CopyArg;

private:
    struct NeverMatched {};
    typedef const StoredType& ImplicitArg;
    typedef typename GnT::Select<false, const StoredType&, NeverMatched>::Result ExplicitArg;

public:

    inline GnSmartPointer(){}

    explicit
    GnSmartPointer(ExplicitArg p) : SP(p){}

    inline GnSmartPointer(ImplicitArg p) : SP(p){}

    inline GnSmartPointer(CopyArg& rhs) : SP(rhs), OP(rhs), KP(rhs)
    {
        Clone(rhs);
    }

    template<typename T1, template <class> class OP1, template <class> class SP1>
    inline GnSmartPointer(const GnSmartPointer<T1, OP1, SP1 >& rhs) : SP(rhs), OP(rhs)
    {
		Clone(rhs);
	}

    template<typename T1, template <class> class OP1, template <class> class SP1>
    inline GnSmartPointer(GnSmartPointer<T1, OP1, SP1 >& rhs) : SP(rhs), OP(rhs)
    {
        Clone(rhs);
    }

	inline GnSmartPointer& operator=(ImplicitArg pointee)
	{
		Release();
		return Reset(pointee);
	}

    inline GnSmartPointer& operator=(CopyArg& rhs)
    {
        Release();
		Clone(rhs);
        return *this;
    }

    template<typename T1, template <class> class OP1, template <class> class SP1>
    inline GnSmartPointer& operator=(const GnSmartPointer<T1, OP1, SP1 >& rhs)
    {
        Release();
		Clone(rhs);
        return *this;
    }

    template<typename T1, template <class> class OP1, template <class> class SP1>
    inline GnSmartPointer& operator=(GnSmartPointer<T1, OP1, SP1 >& rhs)
    {
        Release();
		Clone(rhs);
        return *this;
    }


	GNFORCEINLINE virtual ~GnSmartPointer()
    {
        Release();
    }

	GNFORCEINLINE PointerType operator->()
    {
        GnAssert(GetImplRef(this));
        return SP::operator->();
    }

    GNFORCEINLINE ReferenceType operator*()
    {
        GnAssert(GetImplRef(this));
        return SP::operator*();
    }

    GNFORCEINLINE bool operator!() const 
    {
		return GetImpl(this) == 0;
	}

    GNFORCEINLINE static objectT* GetPointer(const GnSmartPointer& sp)
    {
		return GetImpl( sp );
	}

    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator==(const GnSmartPointer<T1, OP1, SP1 >& rhs) const
    {
		return GetImpl(this) == GetImpl(rhs);
	}
    
    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator!=(const GnSmartPointer<T1, OP1, SP1 >& rhs) const
    {
		return !(*this == rhs);
	}

    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator<(const GnSmartPointer<T1, OP1, SP1 >& rhs) const
    {
		return GetImpl(this) < GetImpl(rhs);
	}

    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator > (const GnSmartPointer< T1, OP1, SP1 > & rhs)
    {
        return ( GetImpl(rhs) < GetImpl(this) );
    }

    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator <= (const GnSmartPointer< T1, OP1, SP1 >& rhs)
    {
        return !( GetImpl(rhs) < GetImpl(this) );
    }

    template<typename T1, template <class> class OP1, template <class> class SP1>
    GNFORCEINLINE bool operator >= (const GnSmartPointer< T1, OP1, SP1 >& rhs)
    {
        return !( GetImpl(this) < GetImpl(rhs) );
    }

protected:
	GNFORCEINLINE GnSmartPointer& Reset(ImplicitArg pointee)
	{
		OP::Reset(pointee);
		SP::Reset(pointee);
		return *this;
	}

	GNFORCEINLINE void Release()
	{
		if (OP::Release(GetImpl(*static_cast<SP*>(this))))
			SP::Destroy();
	}
	GNFORCEINLINE void Clone(GnSmartPointer& rhs)
	{
		OP::Clone(rhs);
		SP::Clone(rhs);
	}

private:
//    // Helper for enabling 'if (sp)'
//    struct Tester
//    {
//        Tester(int) {}
//        void dummy() {}
//    };
//
//    typedef void (Tester::*unspecified_boolean_type_)();
//
//    typedef typename Select<true, Tester, unspecified_boolean_type_>::Result
//        unspecified_boolean_type;
//
//public:
//    // enable 'if (sp)'
//    operator unspecified_boolean_type() const
//    {
//        return !*this ? 0 : &Tester::dummy;
//    }

private:
//    // Helper for disallowing automatic conversion
//    struct Insipid
//    {
//        Insipid(PointerType) {}
//    };
//
//    typedef typename Select<false, PointerType, Insipid>::Result
//		AutomaticConversionResult;
//
//public:
//    operator AutomaticConversionResult() const
//    { return GetImpl(*this); }
};

#define GnSmartPtr(classname) \
	class classname; \
	typedef GnSmartPointer< classname, RefCounted,DefaultSPStorage> classname##Ptr



#include "GnSmartPointer.inl"
#endif // GNSMARTPOINTER_H
